﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Mrkz.ImageManipulation
{
    public static class ImageFactory<TPixel>
        where TPixel : IEquatable<TPixel>, IEquatableWithTolerance<TPixel>, IArithmeticOperation<TPixel>, IPixel, new()
    {      
        public static TImage<TPixel> FromBitmapSource(BitmapSource src)
        {
            TImage<TPixel> e = new TImage<TPixel>();

            e._width = src.PixelWidth;
            e._height = src.PixelHeight;
            e._cropArea = new Int32Rect(0, 0, e.Width, e.Height);
            e._stride = (e._width * src.Format.BitsPerPixel + 7) / 8;
            e._data = FromInteropPixels(InteropImage<Pixel>.CopyPixels(src));

            return e;
        }

        static TPixel[,] FromInteropPixels(InteropPixel[,] InteropPixels)
        {
            TPixel[,] data = new TPixel[InteropPixels.GetLength(0), InteropPixels.GetLength(1)];

            for (int r = 0; r < InteropPixels.GetLength(0); r++)
                for (int h = 0; h < InteropPixels.GetLength(1); h++)
                {
                    TPixel p = new TPixel();
                    p.Alpha = InteropPixels[r, h].a;
                    p.Blue = InteropPixels[r, h].b;
                    p.Red = InteropPixels[r, h].r;
                    p.Green = InteropPixels[r, h].g;
                    data[r, h] = p;
                }

            return data;
        }

        public static TImage<ComplexPixel> FromImage(TImage<Pixel> src)
        {
            TImage<ComplexPixel> e = new TImage<ComplexPixel>();

            e._width = src.Width;
            e._height = src.Height;
            e._data = new ComplexPixel[e._height, e._width];
            e._cropArea = src.CropArea;
            e._stride = (src.Width * 32 + 7) / 8;

            for (int i = 0; i < e._height; i++)
                for (int j = 0; j < e._width; j++)
                    e._data[i, j] = new ComplexPixel(src.GetPixel(i, j));

            return e;
        }

        public static TImage<TPixel> FromImageWithCrop(TImage<TPixel> src)
        {
            TImage<TPixel> e = new TImage<TPixel>();

            e._data = new TPixel[src.CropArea.Height, src.CropArea.Width];
            e._height = src.CropArea.Height;
            e._width = src.CropArea.Width;
            e._stride = (e.CropArea.Width * 32 + 7) / 8;
            e._cropArea = new Int32Rect(0, 0, e._width, e._height);

            for (int i = 0; i < e._height; i++)
                for (int j = 0; j < e._width; j++)
                {
                    TPixel p= new TPixel();
                    p.Red=src.GetPixel(i, j).Red;
                    p.Green=src.GetPixel(i, j).Green;
                    p.Blue=src.GetPixel(i, j).Blue;

                    e._data[i, j] = p;
                }
            return e;
        }
        
        public static TImage<TPixel> CreateEmptyImage(int Height, int Width, Int32Rect cropArea)
        {
            TImage<TPixel> e = new TImage<TPixel>();

            e._cropArea = cropArea;
            e._data = new TPixel[Height, Width];
            e._height = Height;
            e._width = Width;
            e._stride = (e.CropArea.Width * 32 + 7) / 8;

            return e;
        }

        public static TImage<ComplexPixel> FromComplexPixelArray(int Height, int Width, Int32Rect CropArea,
                                    System.Numerics.Complex[,] out_R, System.Numerics.Complex[,] out_G, System.Numerics.Complex[,] out_B)
        {
            TImage<ComplexPixel> image = ImageFactory<ComplexPixel>.CreateEmptyImage(Height, Width, CropArea);

            Parallel.For(0, Height, (y) =>
            {
                for (int x = 0; x < Width; x++)
                    image.SetPixelWithPad(y, x,
                        new ComplexPixel(out_R[y, x], out_G[y, x], out_B[y, x]));
            });

            return image;
        }

    }

    public static class ImageConverter<TPixelTo,TPixelFrom>
      where TPixelTo : IEquatable<TPixelTo>, IEquatableWithTolerance<TPixelTo>, IArithmeticOperation<TPixelTo>, IPixel, new()
        where TPixelFrom : IEquatable<TPixelFrom>, IEquatableWithTolerance<TPixelFrom>, IArithmeticOperation<TPixelFrom>, IPixel, new()
    {
        public static TImage<TPixelTo> CreateEmptyImage(TImage<TPixelFrom> Model)
        {
            TImage<TPixelTo> e = new TImage<TPixelTo>();

            e._cropArea = Model.CropArea;
            e._data = new TPixelTo[Model._height, Model._width];
            e._height = Model._height;
            e._width = Model._width;
            e._stride = Model._stride;

            return e;
        }

        public static TImage<TPixelTo> FromImage(TImage<TPixelFrom> src)
        {
            TImage<TPixelTo> e = new TImage<TPixelTo>();

            e._width = src.Width;
            e._height = src.Height;
            e._data = new TPixelTo[e._height, e._width];
            e._cropArea = src.CropArea;
            e._stride = src._stride;

            for (int i = 0; i < e._height; i++)
                for (int j = 0; j < e._width; j++)
                {
                    TPixelTo p = new TPixelTo();
                    p.Red = src.GetPixel(i, j).Red;
                    p.Green = src.GetPixel(i, j).Green;
                    p.Blue = src.GetPixel(i, j).Blue;

                    e._data[i, j] = p;
                }

            return e;
        }

        public static TImage<TPixelTo> FromImageWithCrop(TImage<TPixelFrom> src)
        {
            TImage<TPixelTo> e = new TImage<TPixelTo>();

            e._data = new TPixelTo[src.Height, src.Width];
            e._height = src.Height;
            e._width = src.Width;
            e._stride = (e.CropArea.Width * 32 + 7) / 8;
            e._cropArea = src.CropArea;

            for (int i = 0; i < e._height; i++)
                for (int j = 0; j < e._width; j++)
                {
                    TPixelTo p = new TPixelTo();
                    p.Red = src.GetPixel(i, j).Red;
                    p.Green = src.GetPixel(i, j).Green;
                    p.Blue = src.GetPixel(i, j).Blue;

                    e._data[i, j] = p;
                }
            return e;
        }   
       

    }
}
